ECMAScript 2015 est approuvé

Posté par  . Édité par palm123, Kaane, BAud, ZeroHeure et DontShootMe. Modéré par Nils Ratusznik. Licence CC By‑SA.
Étiquettes : aucune
55
29
juin
2015
JavaScript

La spécification ECMAScript 6e édition a été approuvée le 17 juin 2015 sous le nom ECMAScript 2015.

Parmi les nouveautés, il faut noter l'arrivée des classes, des modules, des fonctions fléchées, des boucles for-of, des générateurs, des proxies et de plusieurs nouvelles méthodes.

Sommaire

Cette nouvelle édition permet de rattraper le retard face au JavaScript déjà implémenté dans les navigateurs. De ce fait, l'arrivée des mots clés const et let n'est pas si nouvelle. Par contre, la spécification ne correspond pas toujours aux implémentations, et certaines fonctionnalités devront être revues.

Constante et variable

La déclaration const permet de créer une constante nommée accessible uniquement en lecture.

const PI = 3.141593;
PI > 3.0;

L'instruction let permet de déclarer une variable dont la portée est celle du bloc courant, éventuellement en initialisant sa valeur.

function letTest() {
    let x = 31;
    if (true) {
        let x = 71;  // c'est une variable différente
        console.log(x);  // 71
    }
    console.log(x);  // 31
}

Classe

Durant le développement de la spécification, plusieurs propositions ont été élaborées pour la gestion des classes. La solution retenue est plutôt minimale. La classe contient un constructeur et des méthodes. L'héritage simple est géré. À cela s'ajoutent des méthodes get et set, et des membres statiques.

class Fruit {
    constructor (couleur) {
        this.couleur = couleur;
    }
    static choisirCouleur() {
        return ['rouge', 'jaune', 'bleu', 'vert', 'orange', 'mauve'];
    }
    toString () { 
        return "fruit";
    }
}

class Pomme extends Fruit {
    constructor (couleur, sorte) {
        super(couleur);
        this.sorte = sorte;
    }
    get saveur() {
        return this.sorte + " " + this.couleur;
    }
    toString () { 
        return super.toString() + " : pomme";
    }
}

let maPomme = new Pomme('jaune', 'Délicieuse');

Fonction fléchée

Une expression de fonction fléchée (arrow function en anglais) permet d'avoir une syntaxe plus courte que les expressions de fonction et permet de lier la valeur this de façon lexicale. Les fonctions fléchées sont obligatoirement anonymes.

Les fonction fléchées peuvent être utilisées suivant deux syntaxes différentes :

param => expression

qui est équivalente à

function(param) {return param.expression}

ou sous la forme

([param] [, param]) => {instructions}

qui est équivalente à

function([param] [, param]) {
   instructions
}

Bien que la fonction fléchée soit nécessairement anonyme elle-même, elle peut néanmoins être mappée sur une variable.

var simple = a => a > 15 ? 15 : a;
var complexe = (a, b) => {
    if (a > b) {
        return a;
    } else {
        return b;
    }
}

Une fonction fléchée ne définit pas son propre this. Elle utilise celui disponible dans son contexte.

var robert = {
    _nom: "Robert",
    _amis: [],
    imprimerAmis() {
        this._amis.forEach(f =>
        console.log(this._nom + " connaît " + f));
    }
}

Affecter par décomposition

L'affectation par décomposition (destructuring en anglais) est une expression qui permet d'extraire des données d'un tableau ou d'un objet grâce à une syntaxe dont la forme ressemble à la structure du tableau ou de l'objet. La syntaxe est semblable à ce qu'offre Perl ou Python.

Décomposition d'un tableau

var list = [ 1, 2, 3 ];
var [ a, , b ] = list;
[ b, a ] = [ a, b ];

Décomposition d'un objet

var o = {p: 42, q: true};
var {p, q} = o;

console.log(p); // 42
console.log(q); // true 

var {p: toto, q: truc} = o;

console.log(toto); // 42
console.log(truc); // true

Opérateur de décomposition

L'opérateur de décomposition permet de développer une expression lorsque plusieurs arguments ou plusieurs éléments sont nécessaires.

var params = [ "salut", true, 7 ];
var other = [ 1, 2, ...params ]; // [ 1, 2, "salut", true, 7 ]
f(1, 2, ...params) === 9;

var str = "foo";
var chars = [ ...str ]; // [ "f", "o", "o" ]

Paramètres

Un paramètre du reste permet de représenter un nombre indéfini d'arguments sous forme d'un tableau.

function f (x, y, ...a) {
    return (x + y) * a.length;
}
f(1, 2, "salut", true, 7) === 9;

Un paramètre par défaut permet d'initialiser des paramètres lors de l'appel de la fonction si la valeur passée est nulle (au sens où il n'y a aucune valeur de passée) ou undefined.

function f (x, y = 7, z = 42) {
    return x + y + z;
}
f(1) === 50;

Décomposition

Les valeurs des tableaux et les propriétés des objets passés en paramètre à une fonction seront décomposées.

function f ([ name, val ]) {
    console.log(name, val);
}
function g ({ name: n, val: v }) { 
    console.log(n, v);
}
function h ({ name, val }) {
    console.log(name, val);
}
f([ "bar", 42 ]);
g({ name: "foo", val: 7 });
h({ name: "bar", val: 42 });

Nouvelles notations

Raccourcis pour les noms de propriétés

Une notation raccourcie permet de créer une propriété qui porte le nom de sa variable d'origine.

var a = "toto", b = 42, c = {};
var o = { a, b, c };

Raccourcis pour les noms de méthodes

Une notation raccourcie permet de ne plus utiliser le mot-clé function.

var o = {
    property([parameters]) {},
    get property() {},
    set property(value) {},
    * generator() {}
};

Noms calculés pour les propriétés

Une expression entre crochets [] peut être calculée en tant que nom d'une propriété.

var prop = "toto";
var o = {
    [prop]: "hey",
    ["tr" + "uc"]: "ho"
};

Itérateur

L'instruction for-of permet de créer une boucle qui parcourt un objet itérable.

let fibonacci = {
    [Symbol.iterator]() {
        let pre = 0, cur = 1;
        return {
            next () {
                [ pre, cur ] = [ cur, pre + cur ];
                return { done: false, value: cur };
            }
        };
    }
}

for (let n of fibonacci) {
    if (n > 1000)
        break;
    console.log(n);
}

Générateur

Un générateur est une fonction qu'il est possible de quitter puis de reprendre. Le contexte d'un générateur (les liaisons avec ses variables) est sauvegardé entre les reprises successives.

function* range (start, end, step) {
    while (start < end) {
        yield start;
        start += step;
    }
}

for (let i of range(0, 10, 2)) {
    console.log(i); // 0, 2, 4, 6, 8
}

Promise

L'objet Promise (pour « promesse ») est utilisé pour réaliser des opérations de façon asynchrone. Une promesse est dans un de ces états :

  • en attente : état initial, la promesse n'est ni remplie, ni rompue ;
  • tenue : l'opération a réussi ;
  • rompue : l'opération a échoué ;
  • acquittée : la promesse est tenue ou rompue mais elle n'est plus en attente.

Le constructeur de l'objet Promise prend en argument une fonction avec deux paramètres : resolve et reject. Si la promesse est tenue, on appelle la fonction resolve() avec le résultat. Si elle est rompue, on appelle la fonction reject() avec la raison.

var p1 = new Promise((resolve, reject) => {
    setTimeout(() => resolve(1), 1000);
});

La méthode then() prend deux arguments qui sont deux fonctions callback à utiliser en cas de complétion ou d'échec de la Promise. Elle renvoie une Promise, on peut donc facilement enchaîner plusieurs appels à cette méthode.

p1.then((valeur) => {
    console.log(valeur); // 1
    return valeur + 1;
}).then((valeur) => {
    console.log(valeur); // 2
});

Module

Les modules permettent de créer de nouveaux espaces de noms, et ainsi limiter la pollution de l'espace de nom global.

Import

// someApp.js
import * as math from "lib/math";
console.log("2π = " + math.sum(math.pi, math.pi));

Export

// lib/math.js
export function sum (x, y) { return x + y };
export var pi = 3.141593;

Réflexion

Proxy

L'objet Proxy est utilisé afin de définir un comportement sur mesure pour certaines opérations fondamentales (par exemple, l'accès aux propriétés, les affectations, les énumérations, les appels de fonctions, etc.).

var handler = {
    get: function(cible, nom){
        return nom in cible ? cible[nom] : 37;
    }
};

var p = new Proxy({}, handler);
p.a = 1;
p.b = undefined;

p.a === 1;
p.b === undefined;
'c' in p === false;
p.c === 37;

Reflect

Reflect est un objet natif qui fournit des méthodes pour les opérations qui peuvent être interceptées (via les proxies). Reflect n'est pas une fonction (y compris pour construire un objet).

let obj = { a: 1 };
Object.defineProperty(obj, "b", { value: 2 });
obj[Symbol("c")] = 3;
Reflect.ownKeys(obj); // [ "a", "b", Symbol(c) ]

Nouvelles méthodes

Voici quelques exemples de nouvelles méthodes qui ont été ajoutées aux objets standards.

Object

La méthode Object.assign() est utilisée afin de copier les valeurs de toutes les propriétés directes (non héritées) d'un objet qui sont énumérables sur un autre objet cible.

var dst = { quux: 0 };
var src1 = { foo: 1, bar: 2 };
var src2 = { foo: 3, baz: 4 };
Object.assign(dst, src1, src2);

La méthode Object.setPrototypeOf() définit le prototype d'un objet donné avec un autre objet ou null.

function Parent(){}
function Child(){}
Object.setPrototypeOf(Child.prototype, Parent.prototype);
new Child instanceof Child;  // true
new Child instanceof Parent; // true

Array

La méthode find() renvoie une valeur contenue dans le tableau si un élément du tableau respecte une condition donnée par la fonction de test passée en argument.

[ 1, 3, 4, 2 ].find(x => x > 3); // 4

String

Les méthodes startsWith(), endsWith() et includes() renvoient un booléen indiquant, respectivement, si la chaîne de caractères commence par, se termine par ou contient la deuxième chaîne de caractères fournie en argument.

"hello".startsWith("ello", 1); // true
"hello".endsWith("hell", 4); // true
"hello".includes("ell"); // true
"hello".includes("ell", 1); // true
"hello".includes("ell", 2); // false

La méthode repeat() construit et renvoie une nouvelle chaîne de caractères qui contient le nombre de copies demandées de la chaîne de caractères sur laquelle la méthode a été appelée, concaténées les unes aux autres.

" ".repeat(4 * depth);
"foo".repeat(3);

Number

La méthode Number.isSafeInteger() permet de déterminer si la valeur, passée en argument, est un entier représentable correctement en ECMAScript (c'est-à-dire un nombre compris entre -(253 -1) et 253 -1).

Number.isSafeInteger(42) === true;
Number.isSafeInteger(9007199254740992) === false;

Autres fonctionnalités

  • Les nombres octaux et binaires sous une forme littérale
  • Les tableaux typés
  • Les gabarits de chaînes de caractères
  • Les objets Map et Set et leurs équivalents avec références faibles
  • Amélioration de la gestion de l'Unicode pour les chaînes de caractères et les expressions rationnelles

Il ne reste plus qu'à attendre que toutes ces nouveautés arrivent jusqu'à votre navigateur préféré.

Conversion

La conversion des nouveautés du ECMAScript 2015 vers le langage compris par les précédentes implémentations peut se faire à l'aide d'outils. Plusieurs nouveautés peuvent être implémentées par ECMAScript lui-même.

Compilateur

Il est possible de faire appel à un compilateur pour convertir les nouvelles fonctionnalités du langage vers du code compatible avec les anciennes implémentations. Il est plus facile de compiler le code depuis le serveur et envoyer seulement le résultat au client.

Babel est un de ces compilateurs. Tous les exemples proposés ici compilent avec Babel. Une application Web permet de tester ses connaissances depuis un navigateur sans installer quoi que ce soit. Par contre, elle ne traduit pas les nouvelles méthodes. Il faut faire appel à d'autres outils pour avoir une application fonctionnelle.

API

Des bibliothèques sont disponibles pour l'implémentation des nouvelles API.

core-js implémente les nouveaux objets et les nouvelles méthodes du langage. Elle s'utilise en complément d'un compilateur comme Babel.

Aller plus loin

  • # Jolie dépêche

    Posté par  (site web personnel) . Évalué à 10.

    Dommage que je ne l'ai pas vue plus tôt pour y participer.
    Alors ma petite contribution d'un lundi matin: les devs de Babel ont réalisé eux aussi une présentation assez réussie des nouveautés d'ES6.
    Et pour ceux qui ne connaissent pas encore, il existe un fork de Node.js qui accepte la syntaxe d'ES6: il s'agit d'Io.js
    J'ai envie de dire qu'il s'agit d'un fork doux puisque les développements seront repris en grande partie par Joyent pour être ensuite intégrés à Node.

    Bonne semaine à tous.

    • [^] # Re: Jolie dépêche

      Posté par  (site web personnel) . Évalué à 10.

      J'ai envie de dire qu'il s'agit d'un fork doux puisque les développements seront repris en grande partie par Joyent pour être ensuite intégrés à Node.

      Pas exactement. Io.js est un fork réalisé par la plupart des gros contributeurs de nodejs, car ils considèrent que Joyent ne mène pas le projet Node.js de la bonne façon et ne tient pas assez compte de la communauté. Io.js a connu un démarrage fulgurent et le nodejs de Joyent n'avance plus que très lentement. Du coup, Joyent a accepté que la marque "Node.js" passe à une fondation Node.js et les règles de cette fondation ont été établies par des discussions assez longues entre Joyent et les contributeurs d'Io.js. À moyen terme, il n'y aura qu'une seule version, node.js, qui avancera avec un mode ouvert tel que défini par la fondation Node.js. Et d'ici là, on continue de voir quelques nouvelles versions d'iojs et du nodejs de Joyent.

      • [^] # Re: Jolie dépêche

        Posté par  . Évalué à 2.

        Donc en gros le mec à l'origine de Node.js (Joyent) est un développeur qui n'a pas un ego surdimensionné ?
        Les jeunes ne respectent plus rien !

        • [^] # Re: Jolie dépêche

          Posté par  (site web personnel) . Évalué à 10.

          Donc en gros le mec à l'origine de Node.js (Joyent) est un développeur qui n'a pas un ego surdimensionné ?

          C'est plus compliqué que ça. Joyent est une entreprise. Le mec à l'origine de Node.js s'appelle Ryan Dahl.

          À un moment, Node.js a commencé à avoir un peu de visibilité et Joyent a embauché Ryan Dahl et d'autres contributeurs importants pour qu'ils puissent bosser à temps plein dessus. Au bout d'un moment, Ryan Dahl en a eu marre et a arrêté de travailler sur Node.js. Il a passé la main à des personnes en qui il avait confiance sur Joyent. Puis, les années passant, les personnes ont changé, Joyent a resserré sa prise sur la marque Node.js, le développement du coeur de Node.js s'est ralenti. Et les principaux contributeurs ont voulu pousser pour que la roadmap de Node.js ne soit plus dicté par Joyent, qu'il y ait plus souvent des releases, que certaines personnes qui travaillent sur d'autres choses que le code soient reconnus, que le développement de Node.js soit plus tourné vers la communauté, etc. D'où le fork Io.js, qui a finit par faire plier Joyent.

          Pour en revenir au commentaire ci-dessus, le mec à l'origine de Node.js, Ryan Dahl, n'avait pas un ego surdimensionné, loin de là. Et les jeunes qui ont poussé le fork de Node.js (et les moins jeunes aussi) sont surement plus respectueux du travail de Ryan Dahl que pouvait l'être Joyent ces derniers temps. Joyent a bien aidé Node.js à un moment, mais cela fait quand même un bout de temps qu'ils sont plus un frein qu'autre chose.

    • [^] # Re: Jolie dépêche

      Posté par  (site web personnel) . Évalué à 4.

      Bonjour,

      Babel est aussi un protocole de routage.

      http://www.bortzmeyer.org/6126.html
      http://www.pps.univ-paris-diderot.fr/~jch/software/babel/

      La dernière version permet de multi-homing (pardon pour l'anglicisme), c'est à dire d'utiliser à la fois une connexion ADSL et une connexion par la "data" GSM (par exemple).

      Donc, les dev du protocole de routage Babel on fait un chouette travail.
      Et les dev du langage Babel ont réalisés une présentation réussi.

      Pourquoi bloquer la publicité et les traqueurs : https://greboca.com/Pourquoi-bloquer-la-publicite-et-les-traqueurs.html

  • # Chouette

    Posté par  (site web personnel) . Évalué à 3.

    Merci pour cette dépêche!

    C'est chouette de voir des fonctions comme les promises ou les modules qui existent depuis longtemps dans des implémentations variées arriver dans le standard. Les proxies et la réflection sont aussi prometteurs de structures abstraites plus faciles à utiliser.

    Certains point me convainquent beaucoup moins, comme par exemple la petite surcouche objet puisque le livre de Douglas Crockford *JavaScript, the good parts” répertorie des techniques faciles à utiliser qui permettent de vivre en paix avec le modèle objet plutôt exotique de JavaScript… il ne semblait pas urgent de rendre encore plus compliqué ce qui l'était déjà assez.

    Pour l'assignement d'objets, c'est dommage de ne pas avancer plus loin en proposant aussi la copie en profondeur des objets – qui marche au moins pour les PODs – c'est du jargon C++, je veux dire les structures sans méthodes. Je ne vois aussi pas trop l'intérêt des itérables, puisque JavaScript est un langage fonctionnel et que la bibliothèque standard propose des tableaux qui savent faire reduce ou forEach la fonctionnalité des itérables me semble un peu superflue, et demande de comprendre des nouveaux concepts (comment marche le 'yield') alors qu'utiliser reduce ou forEach rentre dans le cadre normal du langage.

    • [^] # Re: Chouette

      Posté par  (site web personnel, Mastodon) . Évalué à 4.

      Je ne vois aussi pas trop l'intérêt des itérables, puisque JavaScript est un langage fonctionnel et que la bibliothèque standard propose des tableaux qui savent faire reduce ou forEach la fonctionnalité des itérables me semble un peu superflue, et demande de comprendre des nouveaux concepts (comment marche le 'yield') alors qu'utiliser reduce ou forEach rentre dans le cadre normal du langage.

      L’intérêt c'est de rendre un objet qui puisse être parcouru comme un tableau, et donc qu'on puisse en donner une instance à du code qui sait itèrer sur un tableau classique. Cela encapsule donc la manière dont sont structurées les données de l'objet, et le code utilisateur n'a ainsi pas besoin de connaître cette structure pour itérer sur les "éléments" de l'objet.

      Quant à connaitre l'utilisation de yield (et par conséquent les générateurs), il est important d'en comprendre le concept : cela permet de générer une liste d’élément à la demande : si on itère sur une liste d'éléments, on génère juste les éléments demandées, et pas toute une liste dont on n'est pas sûr qu'elle soit entièrement utilisée. Cela peut faire donc économiser énormément de mémoire et de traitements.

      • [^] # Re: Chouette

        Posté par  (site web personnel) . Évalué à 2. Dernière modification le 29 juin 2015 à 14:51.

        Pour des implémentations fictives d'un objet représentant un intervalle, qu'est-ce que c'est l'avantage de dire

        var sequence = createSequence(1,10);
        for(let i of sequence(0 {
          print(i);
        }
        

        par rapport à

        var sequence = createSequence(1,0);
        sequence.forEach(print);
        

        Si le traitement est plus compliqué on peut remplacer le print du deuxième exemple par une fonction anonyme. La deuxième forme est rendue possible par le langage depuis longtemps (toujours?) et je ne vois pas trop l'intérêt d'étendre le langage pour proposer de faire de façon compliquée ce qu'on pouvait faire simplement avant.

        En plus l'intérêt de la deuxième forme est qu'on peut implémenter facilement le forEach pour éviter d'avoir un état mutable dans sequence ce qui permet de partager la valeur entre plusieurs threads sans se casser la tête. La forme avec yield n'a pas cet avantage.

        La forme avec yield a peut-être des avantages sur le forEach mais ce n'est pas celui que tu dis. Rien n'interdit à un objet de publier des fonctions forEach ou la forme plus générale reduce pour permettre à tout le monde d'itérer sur la propriétés de l'objet de façon standardisée. Ou de générer les éléments à la demande.

        Tout ce que tu dis n'est pas spécifique au yield et est déjà rendu possible par les fonctions d'ordre supérieur à la reduce et forEach.

        • [^] # Re: Chouette

          Posté par  (site web personnel, Mastodon) . Évalué à 2.

          Le problème de l'implémentation de ton objet séquence, c'est que ta séquence ne fonctionnera pas avec Map, WeakMap, Set etc.

          Implémenter une méthode forEach sur un objet représentant une séquence, ne le fait pas reconnaitre pour autant comme une séquence JS, et donc ne le rend pas utilisable avec les fonctions/structures syntaxiques manipulant des itérables. Concrètement, l'objet "itérable" peut se faire ainsi passer pour un Array. Par contre un objet simple implémentant foreach, non.

          https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols

          En d'autres termes, Iterable et Iterator spécifie une manière standard de faire des objets iterables. Tu peux voir ça comme une interface. Un peu comme en PHP (et d'autres langages…), où un objet qui implémente l'interface Iterator, peut être passé à toutes les fonctions qui manipulent des array.

          En plus l'intérêt de la deuxième forme est qu'on peut implémenter facilement le forEach pour éviter d'avoir un état mutable dans sequence ce qui permet de partager la valeur entre plusieurs threads sans se casser la tête. La forme avec yield n'a pas cet avantage.

          yield n'est pas obligatoire pour faire un Iterable en JS. Ton objet peut être imutable. Tout ceci dépend de ton implementation.

          • [^] # Re: Chouette

            Posté par  (site web personnel) . Évalué à 2.

            yield n'est pas obligatoire pour faire un Iterable en JS. Ton objet peut être imutable. Tout ceci dépend de ton implementation.

            J'ai lu un peu rapidement et fait une confusion entre iterable et générateurs. La question que je pose est à quoi sert yield qui demande une extension syntaxique alors qu'on peut apparemment se passer de générateurs en implémentant des fonctions d'ordre supérieur de type reduce ou forEach. Est-ce qu'il y a des cas qu'on ne peut que traiter avec un générateur?

            • [^] # Re: Chouette

              Posté par  (site web personnel, Mastodon) . Évalué à 3.

              Est-ce qu'il y a des cas qu'on ne peut que traiter avec un générateur?

              je n'en ai pas en tête.

              Mais, dans ton cas où tu implémentes ta fonction forEach, syntaxiquement, ce n'est pas reconnu par JS comme étant un générateur, quand bien même ton forEach serait "lazy". Ton forEach n'est pas une manière standard de faire un Iterateur.

              D'ailleurs avec ton implementation de forEach pour les nombres premiers, je ne suis pas vraiment fan. Par exemple, je veux récupérer les 10 premiers nombres premiers. Je suis obligé de faire

              var primes = createPrimes();
              var i = 10;
              var sum = 0;
              try {
                 primes.forEach(function (np){
                     // do something with np
                     sum += np;
                     // finished ?
                     if (--i == 0) {
                         throw new Error("stop")
                     }
                 });
              }
              catch(e){
                 // ne pas oublier le catch, sinon...
              }
              

              Alors que si createPrimes() retournait un iterateur ou génerateur

              var primes = createPrimes();
              var i=0;
              var sum = 0;
              var np = primes.next();
              while ( --i>0 && !np.done){
                  // do something with np
                  sum += np.value;
                  // next...
                  np = primes.next();
              }
              

              Pour moi la solution avec iterateur/générateur me semble plus compacte et lisible…

      • [^] # Re: Chouette

        Posté par  (site web personnel) . Évalué à 2.

        Pour donner un exemple concret de définition d'un objet itérable au moyen d'un forEach j'ai défini un objet primes qui énumère les nombres premiers (SpiderMonkey / js) – de la façon la plus naïve possible:

        var createPrimes = function(spec, _my) {
            var that = {};
            var my = _my || {};
        
            my.knownPrimes = [ 2 ];
            my.upperBound = 2;
        
            var isDivisible = function(n) {
                return my.knownPrimes.some(function(k) {
                    return (n % k) === 0;
                });
            };
        
            var findPrimesUpTo = function(n) {
                for(var i = my.upperBound; i <=n; ++i) {
                    if(! isDivisible(i)) {
                        my.knownPrimes.push(i);
                    }
                }
                my.upperBound = n;
            }
        
            var isPrime = function(n) {
                findPrimesUpTo(n);
                return my.knownPrimes.some(function(k) {
                    return k === n;
                });
            }
        
            that.forEach = function(callback) {
                var i = 2;
                while(true) {
                    if(isPrime(i)) {
                        callback(i);
                    };
                    ++ i
                };
            };
        
            return that;
        };
        
        var primes = createPrimes();
        primes.forEach(print);
        

        Cela ne s'arrête jamais, mais si le code utilisant l'énumération en a marre, il peut lancer une exception par exemple. On peut partager le même énumérateur entre plusieurs threads dans node.js par exemple. Qu'est-ce qu'apporte une réécriture avec yield dans cet exemple? Et dans d'autres exemples?

        • [^] # Re: Chouette

          Posté par  (site web personnel) . Évalué à 3.

          Cela ne s'arrête jamais, mais si le code utilisant l'énumération en a marre, il peut lancer une exception par exemple.

          Une exception pour arrêter une boucle ? Erk.
          Tu vois autre chose pour arrêter la boucle dans ce cas là ?

          • [^] # Re: Chouette

            Posté par  (site web personnel) . Évalué à 2.

            Par exemple on peut configurer l'objet a la création pour lui fixer une borne supérieure. Je voulais juste monter comment définir un forEach qui ne fonctionne de façon paresseuse, sans faire trop attention aux autres détails.

            • [^] # Re: Chouette

              Posté par  (site web personnel) . Évalué à 4.

              Ok ok.
              Dans ton cas, tu génères une séquence en une fois, sans "coupure" du traitement, mais pour moi, les générateurs (donc les itérateurs, quelque part) présentent l'avantage de pouvoir s'associer aux promises pour faire du code sous forme pseudo-synchrone. Ca ouvre des possibilités assez sensationnelles pour injecter de l'asynchronicité dans un long traitement écrit sous forme synchrone.
              Je répond peut-être à côté de la plaque ?

              • [^] # Re: Chouette

                Posté par  (site web personnel) . Évalué à 2.

                Je ne vois pas trop bien ce que tu veux dire par programmation pseudo synchrone mais cela n'a certainement rien de spécifique aux générateurs. Si la génération d'un nouvel élément dure longtemps, on peut très bien demander son calcul à une promise qui se chargera d'appeler le callback et de demander la génération du prochain élément au moment voulu.

                Je n'ai jamais vu un exemple où les générateurs apportaient quelque chose par rapport à une fonction de type forEach, et apparemment ce n'est pas pour aujourd'hui! :)

                Tu as un exemple relativement petit de programmation pseudo-synchorne avec des générateurs et des promises?

                • [^] # Re: Chouette

                  Posté par  (site web personnel) . Évalué à 3.

                  Si la génération d'un nouvel élément dure longtemps, on peut très bien demander son calcul à une promise qui se chargera d'appeler le callback et de demander la génération du prochain élément au moment voulu.

                  Et tu dois donc penser l'écriture de ton traitement de façon asynchrone.

                  Ici yield te permettrait d'insérer des points d'entrées dans ton algorithme de façon à ce que tu te dises "ici on a besoin d'attendre un next()" pour pouvoir continuer le traitement. On y gagne en concision et en expressivité.
                  Les promises c'est sympa, j'aime bien, mais quand il s'agit de véhiculer le contexte d'exécution de toute la promise chain simplement, il faut en passer par des variables extérieures, etc.

                  Tu as un exemple relativement petit de programmation pseudo-synchorne avec des générateurs et des promises?

                  Qu'as-tu donc contre la myriade d'exemples qu'on peut trouver sur le web ? :)

                  Rien que l'exemple sur la page d’accueil de cette lib (qui gagne en popularité en ce moment) : http://taskjs.org/

                  • [^] # Re: Chouette

                    Posté par  (site web personnel) . Évalué à 3.

                    Qu'as-tu donc contre la myriade d'exemples qu'on peut trouver sur le web ? :)

                    Je n'ai rien contre, je veux juste comprendre à quoi ça sert. Mon parcours de programmeur avant le JavaScript contient plein de choses, et beaucoup de fonctionnel (surtout OCaml et un peu de Scheme). Dans ses langages on a des streams et des catamorphismes (le nom pédant de forEach et reduce et consorts.) et on pourrait utiliser les même mécanismes en JavaScript. Mais on préfère introduire une nouvelle syntaxe pour implémenter ces fonctionnalités et je veux juste comprendre pourquoi. C'est de la curiosité, tout simplement.

                    (Pour comprendre pourquoi une solution à un problème est intéressante, la meilleure méthode est d'essayer de résoudre soi-même le problème.)

    • [^] # Re: Chouette

      Posté par  (site web personnel) . Évalué à 6. Dernière modification le 29 juin 2015 à 17:42.

      PODs – c'est du jargon C++, je veux dire les structures sans méthodes.

      N'importe quoi !
      Un POD n'a rien à voir avec des structures sans méthodes !

      // Ceci est un POD bien qu'il ait une méthode:
      class Rectangle {
          public:
             int width, height;
             int area() const { return width * height;  }
      };
      
      // Et ceci n'est pas un POD alors qu'il n'a pas de méthodes
      struct Person {
           std::string nom;
           std::string prenom;
      };

      Un POD est grossomodo un type qui peut être copié avec memcpy car il n'y a pas besoin d'appler un constructeur, destructeur, ou autre operateur qu'il soit explicitement déclaré ou implicite.

      http://en.cppreference.com/w/cpp/concept/PODType
      https://en.wikipedia.org/wiki/Plain_old_data

      --

      Et même dans le contexte du javascript, pourquoi ne pourait-on pas copier les structures qui ont des méthode.

      Par exemple:

      var a = { w: 3, h:4,  area: function() { return this.w * this.h; } }; 
      var b = a;  // ne  serait-ce pas utile d'avoir une vrai copie qui copie toutes les propriétés icnluant la fonction?
      b.w = 34;
      console.log(a.area()); // affiche "136"  (car 'b' et 'a' sont une réference vers le même object
      • [^] # Re: Chouette

        Posté par  (site web personnel) . Évalué à 0.

        N'importe quoi !

        Oui tu as raison d'être plus précis.

        Et même dans le contexte du javascript, pourquoi ne pourait-on pas copier les structures qui ont des méthode.

        Parceque tu ne peux pas copier (au sens de dupliquer) les fonctions, ou plutôt les closures.

      • [^] # Re: Chouette

        Posté par  . Évalué à 1.

        Nimpnawak !

        POD ça sert à faire de la doc en Perl.

Suivre le flux des commentaires

Note : les commentaires appartiennent à celles et ceux qui les ont postés. Nous n’en sommes pas responsables.